package com.lcykcl.ttgen;

import java.util.ArrayList;
import java.util.HashMap;

public class Manager {

	// Completed timetables; FL stands for lectures for the fall semester
	// This collection contains nodes for the completed time table of
	// Lectures/Tutorials for a semester
	// If the time table can't be resolved(i.e., it must have conflict) then it returns conflict time table
	// FLTimeTable => FLCompletedTTBList @nameChanged
	private ArrayList<Node> FLCompletedTTBList;
	private ArrayList<Node> FTCompletedTTBList;
	private ArrayList<Node> SLCompletedTTBList;
	private ArrayList<Node> STCompletedTTBList;

	// Timetable objects for fall and spring semesters
	TimeTable FLttb;
	TimeTable SLttb;
	TimeTable FTttb;
	TimeTable STttb;

	/**
	 * A constructor of Manager Class
	 * 
	 * @param FList
	 * 			The list of two lists of nodes. 
	 *			The first list is for nodes for lectures in fall semester
	 *			The second list is for nodes for tutorials in fall semester
	 * @param SList
	 *			It's same as FList but S stands for the spring session
	 * @param p
	 *			persona
	 * Enum class Persona has 4 kinds of constants which are
	 * Morning/Night/EVEN/PACKED
	 */
	public Manager(ArrayList<ArrayList<Node>> FList,
			ArrayList<ArrayList<Node>> SList, Persona p) {


		//Initiate TimeTable objects
		FLttb = new TimeTable(FList.get(0), null, p);
		SLttb = new TimeTable(FList.get(1), null, p);
		FTttb = new TimeTable(SList.get(0), null, p);
		STttb = new TimeTable(SList.get(1), null, p);

	}

	/**
	 * Update persona for all timetable objects.
	 * The reason it exists is to update
	 * persona information from outside of the manager class.
	 * In that way, we can change the persona without making new manager class 
	 * @param p
	 * persona
	 */
	public void updateAllPersonas(Persona p) {
		FLttb.updatePersona(p);
		SLttb.updatePersona(p);
		FTttb.updatePersona(p);
		STttb.updatePersona(p);
	}

	/**
	 * generate a completed timetable. It sets the instance variables which are
	 * FLCompleteTTBlist/FTCompleteTTBlist/SLCompleteTTBlist/STCompleteTTBlist
	 */
	public void generateCompleteTimeTable() {
		// try to put the timetable for the lectures of both sessions together.
		putBothSessionTogether(FLttb, SLttb);

		//make the time spots selected by the lectures unavailable for the timetable object for tutorials. 
		FTttb.deleteConflictTime(FLttb.getTimeTable());
		STttb.deleteConflictTime(SLttb.getTimeTable());

		// if tutorial timetable for the both sections can't be put together
		// and also, if we can find another timetable for lecture part
		//try to generate a complete timetable again by calling recursive call.
		if (putBothSessionTogether(FTttb, STttb) == false
				&& FLttb.isPossibletoTryAgain()) {
			// update yearSections in the timetable objects for lectures
			// (i.e., we should try different combination of year sections)
			FLttb.tryAgain();
			SLttb.tryAgain();
			generateCompleteTimeTable();
		}
		// return best possible timetables
		FLCompletedTTBList = FLttb.getTimeTable();
		SLCompletedTTBList = SLttb.getTimeTable();
		FTCompletedTTBList = FTttb.getTimeTable();
		STCompletedTTBList = STttb.getTimeTable();
	}

	/**
	 * Returns true if and only if the timetable object fallTimeTable and
	 * springTimeTable are can be put together. 
	 * Putting together means to make one complete timetable for an year
	 * The time table object can have conflict timetable if it's impossible to generate non-conflict timetable
	 * 
	 * @param TimeTable
	 *            t1
	 * @param TimeTable
	 *            t2
	 * @return Boolean
	 */
	private boolean putBothSessionTogether(TimeTable fallTimeTable,
			TimeTable springTimeTable) {
		// generate a timetable for the fall semester first
		fallTimeTable.generateTTB();
		while (!(fallTimeTable.isTTBgenerated()) || fallTimeTable.isPossibletoTryAgain()) {
			fallTimeTable.tryAgain();
			fallTimeTable.generateTTB();
			springTimeTable.tryAgain();
		}
		// generate a timetable for spring semester
		springTimeTable.generateTTB();
		// if spring timetable got conflict and t1 still has combinations in yearSections,
		// then try again for both t1 and t2 and try to put them together
		if (springTimeTable.isConflict() && fallTimeTable.isPossibletoTryAgain()) {
			fallTimeTable.tryAgain();
			springTimeTable.tryAgain();
			putBothSessionTogether(fallTimeTable, springTimeTable);
			// if t1 or t2 got conflict and t1 can't try again to generate the timetable
			// then putting them together is failed and return false
		} else if (springTimeTable.isConflict() || fallTimeTable.isConflict()) {
			return false;
		}
		// if t1 is not conflict and t2 is not conflict then return true
		return true;
	}

	// So main activity calls return fall sibling map to get the courseMap
	public HashMap<String, ArrayList<Node>> getFallSiblingMap() {
		HashMap<String, ArrayList<Node>> fallSiblingMap = this.FLttb
				.getSiblingMap();
		fallSiblingMap.putAll(this.FTttb.getSiblingMap());
		return fallSiblingMap;
	}

	// and main activity calls return fall timetable to get the course list
	public ArrayList<Node> getFallTimeTable() {
		ArrayList<Node> listToReturn = (ArrayList<Node>) this.FLCompletedTTBList
				.clone();
		listToReturn.addAll(FTCompletedTTBList);
		return listToReturn;
	}

	// before the fall timetable activity calls new activity for the spring
	// session it should call this and send this information to the activity
	public HashMap<String, ArrayList<Node>> getSpringSiblingMap() {
		HashMap<String, ArrayList<Node>> springSiblingMap = this.SLttb
				.getSiblingMap();
		springSiblingMap.putAll(this.STttb.getSiblingMap());
		return springSiblingMap;
	}

	// before the fall timetable activity calls new activity for the spring
	// session it should call this and send this information to the activity
	public ArrayList<Node> getSpringTimeTable() {
		ArrayList<Node> listToReturn = (ArrayList<Node>) this.SLCompletedTTBList
				.clone();
		listToReturn.addAll(STCompletedTTBList);
		return listToReturn;
	}
}
